import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.Socket;
import java.util.Scanner;

/**
 */
public class SimpleFtpClient
{
  public static final int DATA_INPUT_PORT = 8900;
  public static final int DATA_OUTPUT_PORT = 8901;
  public static final int FTP_SERVER_PORT = 8902;
  public static final String DATA_OUTPUT_GROUP = "234.2.3.4";

  /**
   * Last sequence number completed by the server
   */
  private int complete = -1;
  
  public static void main(String[] args)
  {
    
    new SimpleFtpClient().runTest("format.xml");
  }
  
  public void runTest(String filename)
  {
    new MulticastListener().start();
    
    Socket s = null;
    try
    {
      s = new Socket("localhost", FTP_SERVER_PORT);
      PrintWriter pw = new PrintWriter(s.getOutputStream());
      Scanner scanner = new Scanner(s.getInputStream());
      String line = scanner.nextLine();
      System.out.println(line);

//      pw.println("PWD");
//      pw.flush();
//      line = scanner.nextLine();
//      System.out.println(line);
      
      System.out.println("sending PASV request");
      pw.println("PASV");
      pw.flush();
      line = scanner.nextLine();
      System.out.println(line);
      // should be (a, b, c, d, e, f)
      // where port number is 256e + f
      int i = line.indexOf("(");
      if (i >= 0)
      {
        line = line.substring(i + 1, line.length() - 1);
        //System.out.println(line);
        String[] h = line.split(",");
        int p = Integer.parseInt(h[4]) * 256 + Integer.parseInt(h[5]);
        System.out.println("found port " + p);    
        System.out.println("sending STOR request");
        pw.println("STOR " + filename);
        pw.flush();
        line = scanner.nextLine();
        System.out.println(line);
        if (line.startsWith("150"))
        {
          Socket temp = new Socket("localhost", p);
          FileInputStream fis = new FileInputStream(filename);
          OutputStream os = temp.getOutputStream();
          int c;
          while ((c = fis.read()) != -1)
          {
            os.write(c);
          }
          os.flush();
          os.close();
          fis.close();

          // should get a 226 message now
          line = scanner.nextLine();
          System.out.println(line);
        }
      }
      
      // send request with sequence number 42
      sendPacket(42, filename);
      
      // wait for completion message
      synchronized(this)
      {
        while (complete < 42)
        {
          try
          {
            wait();
          }
          catch (InterruptedException ignore){}
        }
      }
      
      System.out.println("sending PASV request");
      pw.println("PASV");
      pw.flush();
      line = scanner.nextLine();
      System.out.println(line);
      // should be (a, b, c, d, e, f)
      // where port number is 256e + f
      i = line.indexOf("(");
      if (i >= 0)
      {
        line = line.substring(i + 1, line.length() - 1);
        System.out.println(line);
        String[] h = line.split(",");
        int p = Integer.parseInt(h[4]) * 256 + Integer.parseInt(h[5]);
        System.out.println("found port " + p);    

        String resultFilename = filename + "_result";
        System.out.println("sending RETR request for " + resultFilename);
        pw.println("RETR " + resultFilename);
        pw.flush();
        line = scanner.nextLine();
        System.out.println(line);
        if (line.startsWith("150"))
        {

          Socket temp = new Socket("localhost", p);
          InputStream is = temp.getInputStream();
          Scanner tempScanner = new Scanner(is);
          while (tempScanner.hasNextLine())
          {
            System.out.println(tempScanner.nextLine());
          }
          tempScanner.close();

          // should get a 226 message now
          line = scanner.nextLine();
          System.out.println(line);
        }
      }
//      pw.println("LIST");
//      pw.flush();
//      line = scanner.nextLine();
//      System.out.println(line);
    }
    catch (IOException e)
    {
      System.out.println(e);
    }
    finally
    {
      try
      {
        s.close();
      }
      catch (IOException ignore){}
    }
  }
  
  
  
  private void sendPacket(int sequenceNumber, String filename)
  {
    try
    {
      InetAddress addr = InetAddress.getByName("localhost");
      DatagramSocket s = new DatagramSocket();
      
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      DataOutputStream dos = new DataOutputStream(baos);      
      dos.writeInt(sequenceNumber);
      
      if (filename != null)
      {
        for (int i = 0; i < filename.length(); ++i)
        {
          dos.writeByte(filename.charAt(i));
        }
        dos.writeByte(0); // null terminator
      }
      
      byte[] buf = baos.toByteArray();
      DatagramPacket packet = new DatagramPacket (buf, buf.length, addr, 8900);
      s.send(packet);
    }
    catch (IOException e)
    {
      System.out.println(e);      
    }
  }

  
  private class MulticastListener extends Thread
  {
    public void run()
    {
      while (true)
      {
        MulticastSocket socket = null;
        try
        {         
          socket = new MulticastSocket(DATA_OUTPUT_PORT);
          InetAddress group = InetAddress.getByName(DATA_OUTPUT_GROUP);
          socket.joinGroup(group);
          byte[] buf = new byte[8];
          while (true)
          {
            DatagramPacket packet = new DatagramPacket(buf, buf.length);
            System.out.println("Datagram listener: listening on " + DATA_OUTPUT_PORT);
            socket.receive(packet);
            buf = packet.getData();
            if (packet.getLength() == 8)
            {
              DataInputStream dis = new DataInputStream(new ByteArrayInputStream(buf));
              int seq = dis.readInt();
              int code = dis.readInt();
              System.out.println("Datagram listener: got sequence number " + seq + 
                " result Ox" + Integer.toHexString(code));
              synchronized(SimpleFtpClient.this)
              {
                if (code == 0x200 && seq > complete)
                {
                  complete = seq;
                  SimpleFtpClient.this.notifyAll();
                }
              }
            }
            else
            {
              System.out.println("Datagram listener: got unexpected packet length " + packet.getLength());
              for (int i = 0; i < packet.getLength(); ++i)
              {
                System.out.print(buf[i] + " ");
              }
              System.out.println();
            }
          }

        }
        catch (IOException e)
        {
          System.out.println(e);
        }
        finally
        {
          if (socket != null)
          {
            socket.close();
          }
        }
        // wait a bit before trying again
        try
        {
          Thread.sleep(2000);
        }
        catch (InterruptedException ignore){}
       
      } 
    }

  }

  

}
